#pragma once

#include <stdio.h>

/* here are callback flags to register
 * callback functions needed since we are dealing with async transfer
 * and want to notify frontend about incoming events
 * Can be combined together for one callback to several events.
*/

#define SSH_CALLBACK_CHANNEL_DATA							0x00000001L
#define SSH_CALLBACK_DISCONNECT									0x00000008L
#define SSH_CALLBACK_BANNER												0x00000010L
#define SCP_CALLBACK_DIRNAMES											0x00000020L      /* this one gives pointer to scp_names structure instead of char buffer */
#define SCP_CALLBACK_ERROR												0x00000040L		/* for any error, current operation will be terminated */
#define SCP_CALLBACK_STAT														0x00000080L		/* this one gives pointer to scp_stat structure instead of char buffer */
#define SCP_CALLBACK_FDATA													0x00000100L		/* gives file name and amount of bytes transferred */
#define SCP_CALLBACK_SHUTDOWN										0x00000200L		/* channel closed, failed to open, etc */
#define SCP_CALLBACK_COMPLETED										0x00000400L       /* operation is completed */

/*
 * Mechanism for getting text strings such as usernames and passwords
 * from the front-end.
 * The fields are mostly modelled after SSH's keyboard-interactive auth.
 * FIXME We should probably mandate a character set/encoding (probably UTF-8).
 *
 * Since many of the pieces of text involved may be chosen by the server,
 * the caller must take care to ensure that the server can't spoof locally-
 * generated prompts such as key passphrase prompts. Some ground rules:
 *  - If the front-end needs to truncate a string, it should lop off the
 *    end.
 *  - The front-end should filter out any dangerous characters and
 *    generally not trust the strings. (But \n is required to behave
 *    vaguely sensibly, at least in `instruction', and ideally in
 *    `prompt[]' too.)
 */
typedef struct {
    char *prompt;		/* */
    int echo;		/* should be input be echoed or not. when a password is needed, this is set to zero */
    char *result;	/* allocated/freed by caller */
    size_t result_len;
} prompt_t;

typedef struct {
    /*
     * Indicates whether the information entered is to be used locally
     * (for instance a key passphrase prompt), or is destined for the wire.
     * This is a hint only; the front-end is at liberty not to use this
     * information (so the caller should ensure that the supplied text is
     * sufficient).
     */
    int to_server;
    char *name;		/* Short description, perhaps for dialog box title */
    int name_reqd;	/* Display of `name' required or optional? */
    char *instruction;	/* Long description, maybe with embedded newlines */
    int instr_reqd;	/* Display of `instruction' required or optional? */
    size_t n_prompts;
    prompt_t **prompts;
    void *frontend;
    void *data;		/* slot for housekeeping data, managed by
			 * get_userpass_input(); initially NULL */
} prompts_t;



struct Filename {
    char path[FILENAME_MAX];
};
typedef struct backend_tag Backend;
#ifdef SSHLIB_DLL
typedef struct config_tag Config;
#else
typedef struct config_tag sshConfig;
#endif
typedef struct subsystem_tag Subsystem;


typedef enum {
    /* Actual special commands. Originally Telnet, but some codes have
     * been re-used for similar specials in other protocols. */
    TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,
    TS_EOL,
    /* Special command for SSH. */
    TS_REKEY,
    /* POSIX-style signals. (not Telnet) */
    TS_SIGABRT, TS_SIGALRM, TS_SIGFPE,  TS_SIGHUP,  TS_SIGILL,
    TS_SIGINT,  TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,
    TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,
    /* Pseudo-specials used for constructing the specials menu. */
    TS_SEP,	    /* Separator */
    TS_SUBMENU,	    /* Start a new submenu with specified name */
    TS_EXITMENU	    /* Exit current submenu or end of specials */
} Telnet_Special;

struct telnet_special {
    const char *name;
    int code;
};

struct backend_tag {
    const wchar_t *(*init) (void *frontend_handle, void **backend_handle,
#ifdef SSHLIB_DLL
			 Config *cfg,
#else
			sshConfig *cfg,
#endif
			 char *host, int port, char **realhost, int nodelay,
			 int keepalive);
    void (*free) (void *handle);
    /* back->reconfig() passes in a replacement configuration. */
    void (*reconfig) (void *handle, 
#ifdef SSHLIB_DLL
			 Config *cfg
#else
			sshConfig *cfg
#endif
		);
    /* back->send() returns the current amount of buffered data. */
    int (*send) (void *handle, char *buf, int len, int localid);
    /* back->sendbuffer() does the same thing but without attempting a send */
    int (*sendbuffer) (void *handle, int localid);
    void (*size) (void *handle, int width, int height, int localid);
    void (*special) (void *handle, Telnet_Special code, int localid);
    const struct telnet_special *(*get_specials) (void *handle);
    int (*connected) (void *handle);
    int (*exitcode) (void *handle);
    /* If back->sendok() returns FALSE, data sent to it from the frontend
     * may be lost. */
    int (*sendok) (void *handle);
    int (*ldisc) (void *handle, int);
    void (*provide_logctx) (void *handle, void *logctx);
    /*
     * back->unthrottle() tells the back end that the front end
     * buffer is clearing.
     */
    void (*unthrottle) (void *handle, int, void *c);
    int (*cfg_info) (void *handle);
	int (*create_channel)(void *handle, void *frontend_handle);
	int (*close_channel)(void *handle, int channel);
    int default_port;
};

struct subsystem_tag {
	/* 
		*backend_handle can be initialized by backend init function just before being called .
		In this case subsystem will try to allocate channel by using backend instead of creating new TCP connection
		Please note that the backend will initially try to open primary shell channel, not the subsystem/command one.
		To avoid that, please call subsystem init function first, instead of backend init.
		If *backend_handle contains invalid handle (not NULL), the function will fail.
		DO NOT send backend_handle pointer set to NULL! It has to point to valid memory cell where actual pointer
		will reside. That actual pointer CAN BE null in his time.
		Also, backend and subsystem share common config, so do not try to give second config to the subsystem, 
		it will use backend config anyway if it is initialised already. And of course it will ignore host/port/delay/keepalives
		in that case.
	*/
    const wchar_t *(*init) (void *frontend_handle, void **backend_handle,
#ifdef SSHLIB_DLL
			 Config *cfg,
#else
			sshConfig *cfg,
#endif
			 char *host, int port, char **realhost, int nodelay,
			 int keepalive, int proc, char *remote, wchar_t *local);
    void (*free) (void *handle);
    BOOL (*command) (void *handle, int proc);		/* will return FALSE if cannot execute command */
    int default_port;
};

enum {
    /*
     * SSH-2 key exchange algorithms
     */
    KEX_WARN,
    KEX_DHGROUP1,
    KEX_DHGROUP14,
    KEX_DHGEX,
    KEX_MAX
};

enum {
    /*
     * SSH ciphers (both SSH-1 and SSH-2)
     */
    CIPHER_WARN,		       /* pseudo 'cipher' */
    CIPHER_3DES,
    CIPHER_BLOWFISH,
    CIPHER_AES,			       /* (SSH-2 only) */
    CIPHER_DES,
    CIPHER_ARCFOUR,
    CIPHER_MAX			       /* no. ciphers (inc warn) */
};

struct config_tag {
    /* Basic options */
    char host[512];
    int port;
    int protocol;
    int addressfamily;
    int close_on_exit;
    int warn_on_close;
    int ping_interval;		       /* in seconds */
    int tcp_nodelay;
    int tcp_keepalives;
    /* Proxy options */
    int proxy_type;
    /* SSH options */
    char remote_cmd[512];
    char *remote_cmd_ptr;	       /* might point to a larger command
				        * but never for loading/saving */
    char *remote_cmd_ptr2;	       /* might point to a larger command
				        * but never for loading/saving */
    int nopty;
    int compression;
    int ssh_kexlist[KEX_MAX];
    int ssh_rekey_time;		       /* in minutes */
    char ssh_rekey_data[16];
    int change_username;	       /* allow username switching in SSH-2 */
    int ssh_cipherlist[CIPHER_MAX];
    Filename keyfile;
    int sshprot;		       /* use v1 or v2 when both available */
    int ssh2_des_cbc;		       /* "des-cbc" unrecommended SSH-2 cipher */
    int ssh_no_userauth;	       /* bypass "ssh-userauth" (SSH-2 only) */
    int try_tis_auth;
    int try_ki_auth;
    int ssh_subsys;		       /* run a subsystem rather than a command */
    int ssh_subsys2;		       /* fallback to go with remote_cmd_ptr2 */
    int ssh_no_shell;		       /* avoid running a shell */
    char ssh_nc_host[512];	       /* host to connect to in `nc' mode */
    int ssh_nc_port;		       /* port to connect to in `nc' mode */
    int logomitpass;
    int logomitdata;
    /* SSH bug compatibility modes */
    int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
	sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
	sshbug_pksessid2, sshbug_rekey2;
    /* Telnet options */
    char username[100];
    char termtype[32];
    char termspeed[32];
    char ttymodes[768];		       /* MODE\tVvalue\0MODE\tA\0\0 */
    char environmt[1024];	       /* VAR\tvalue\0VAR\tvalue\0\0 */
    int width, height;

};


struct FilenameW {
    wchar_t path[FILENAME_MAX];
};


enum {
    /* Protocol back ends. (cfg.protocol) */
    PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,
    /* PROT_SERIAL is supported on a subset of platforms, but it doesn't
     * hurt to define it globally. */
    PROT_SERIAL
};


enum {
    /*
     * Network address types. Used for specifying choice of IPv4/v6
     * in config; also used in proxy.c to indicate whether a given
     * host name has already been resolved or will be resolved at
     * the proxy end.
     */
    ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME
};

enum {
    /*
     * Proxy types.
     */
    PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5,
    PROXY_HTTP, PROXY_TELNET, PROXY_CMD
};


enum {
    /*
     * Several different bits of the PuTTY configuration seem to be
     * three-way settings whose values are `always yes', `always
     * no', and `decide by some more complex automated means'. This
     * is true of line discipline options (local echo and line
     * editing), proxy DNS, Close On Exit, and SSH server bug
     * workarounds. Accordingly I supply a single enum here to deal
     * with them all.
     */
    FORCE_ON, FORCE_OFF, AUTO
};

/* SCP stuff */
enum {
	SCP_DOWNLOAD, SCP_UPLOAD, SCP_DIRLIST, SCP_MKDIR, SCP_FSTAT, SCP_DELETE, SCP_RENAME, SCP_SET_STAT, SCP_IDLE
};

/* incoming parameters: 
SCP_DOWNLOAD	- local: name of file/dir to save (wchar_t), remote - name of file/dir to download (char). remote can contain '*'
SCP_UPLOAD			- local: name of file/dir to upload (wchar_t), remote - name of file/dir to save (char). local can contain '*'
SCP_DIRLIST			- local: NULL, remote - name of directory to get list (char).
SCP_MKDIR				- local: NULL, remote - name of directory to create (char).
SCP_FSTAT				- local: NULL, remote - name of file/dir to get stat (char).
SCP_DELETE			- local: NULL, remote - name of file/dir to remote (char).
SCP_RENAME			- local: new file/dir name (char), remote - name of file/dir to rename (char).
SCP_SET_STAT		- local: NULL, remote - attributes (scp_attrib).
SCP_IDLE						- NULL, NULL
*/

struct scp_attrs {
	char name[MAX_PATH];
    unsigned long flags;
	struct {
		 unsigned long hi;
		 unsigned long lo;
	} size;
    unsigned long uid;
    unsigned long gid;
    unsigned long permissions;
    unsigned long atime;
    unsigned long mtime;
};

struct scp_names {
	char longname[MAX_PATH+32];
    struct scp_attrs attrs;
};


/* --------- EXPORTS ---------------- */

/* register_callback(flag, proc) */
typedef void (*SSH_CALLBACK_PROC)(void *frontend, int is_stderr, int channel, const char *data, int len);
typedef BOOL (*REGISTER_CALLBACK_PROC)(int flag, SSH_CALLBACK_PROC proc);

/* This is a general rule: SSH_INPUT_PROC is called when SSH backend needs to input keyboard-interactive data
 * [IN] prompt - a pointer to the structure
 * [IN] data - input string that needs to be added to prompt, optional
 * [IN] len - length of data, optional.
 * data & len are set by the backend when the frontend is sending key pressed in terminal during authentication process.
 * the proc should process 'prompts' structures and fill their result and result_len values. the result must not exeed 100 characters 
 * including trailing zero!
 */

/* register_input(proc) */
typedef int (*SSH_INPUT_PROC)(prompts_t *prompt, unsigned char *data, int len);
typedef BOOL (*REGISTER_INPUT_PROC)(SSH_INPUT_PROC proc);

/* 
    returns pointer to Backend structure if SCP is FALSE.
    returns pointer to SCP structure if SCP is TRUE
*/

/* get_backend(SCP) */
typedef Backend *(*GET_BACKEND_PROC)(int SCP);

/* proc will be called when remote side has closed connection */

/* register_remoteexit(proc) */
typedef void (*NOTIFY_REMOTE_EXIT_PROC)(void *frontend);
typedef BOOL (*REGISTER_REMOTEEXIT_PROC)(NOTIFY_REMOTE_EXIT_PROC proc);

/* init_config(Config) */
#ifdef SSHLIB_DLL
typedef void (*SSH_INIT_CONFIG)(Config *cfg);
#else
typedef void (*SSH_INIT_CONFIG)(sshConfig *cfg);
#endif


/*
*			SSH backend. can be used with sshterm library to provide SSH fully-functional terminal functionality.
			To start backend sucessfully, make following things:
			1. Init cfg structure. Helper function is provided to fill structure with default values (except for host, keyfile, username).
			2. Load the backend. Get backend pointer.
			3. Define callback functions. register_input() is for getting interactive input (usernames, passwords, etc), register_callback
				is for notify frontend about incoming events. SSH_CALLBACK_CHANNEL_DATA means any terminal data. 
				SSH_CALLBACK_DISCONNECT means host is disconnected.
			4. (optional) Connect any frontend if neccessary.
			5. call Backend->init() function in given backend pointer:
				frontend_handle - pointer to application defined structure
				backend_handle - pointer where to store ssh handle
				cfg - pointer to initialized cfg structure
				host, port - obvious
				realhost - where to store pointer to canonical host name
				nodelay - recommendend to set to 1. TCP protocol option
				keepalive - whether to send keepalives or not. Useful when connecting to Solaris boxes.

			After that backend_handle will be generated. All subsequent calls to backend functions should include this handle.

			Hints: Backend->free() call is recommended when sessions finishes. generally - when receiving SSH_CALLBACK_DISCONNECT.
							forgetting to call it will lead to memory leaks.

*/
